package com.nexgo.payment.action.online;

import android.support.annotation.NonNull;

import com.nexgo.data.ChannelType;
import com.nexgo.data.exception.RequestException;
import com.nexgo.data.exception.ResponseException;
import com.nexgo.data.repository.PaymentFactory;
import com.nexgo.domain.interactor.SendAndRecv;
import com.nexgo.domain.repository.PaymentPlatform;
import com.nexgo.iso8583.exception.WriteException;
import com.nexgo.payment.App;
import com.nexgo.payment.R;
import com.nexgo.payment.action.online.upload.Upload;
import com.nexgo.payment.aop.annotation.RunOnMainThread;
import com.nexgo.payment.aop.annotation.RunOnWorkThread;
import com.nexgo.payment.constrant.CardMode;
import com.nexgo.payment.constrant.PreferencesConstants;
import com.nexgo.payment.constrant.TransType;
import com.nexgo.payment.databases.DataSource;
import com.nexgo.payment.databases.SettleInfo;
import com.nexgo.payment.entity.MessageData;
import com.nexgo.payment.entity.TransData;
import com.nexgo.payment.pack.DataBuilder;
import com.nexgo.payment.util.AppUtils;
import com.nexgo.payment.util.BassInfoUtil;
import com.nexgo.payment.util.NetworkUtils;
import com.nexgo.payment.util.StringUtils;
import com.nexgo.sound.SoundManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;

import io.reactivex.observers.DisposableObserver;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Created by xiaox on 2017/7/10.
 */

public class OnlinePresenter implements OnlineContract.Presenter {
    public static final String NEED_CHECK_BASE = PreferencesConstants.NEED_CHECK_BASE;
    private Logger mLog = LoggerFactory.getLogger(OnlinePresenter.class.getSimpleName());

    private final OnlineContract.View mView;
    private final DataSource mRepository;
    private final TransData mTransData;
    private final SoundManager mSoundManager;
    private SendAndRecv mComm;
    private MessageData mMessageData;
    private boolean mIsFirstTime = true;
    private Upload.NowTask mTask = Upload.NowTask.SCRIPT;
    private Upload mUpload;

    public OnlinePresenter(@NonNull OnlineContract.View view, @NonNull DataSource repository, @NonNull TransData transData) {
        mView = checkNotNull(view);
        mRepository = checkNotNull(repository);
        mTransData = checkNotNull(transData);
        mMessageData = new MessageData();
        view.setPresenter(this);
        mSoundManager = new SoundManager();
    }

    @RunOnMainThread
    private void postMainThread(Runnable runnable) {
        if (mView.isActive()) {
            runnable.run();
        }
    }

    @Override
    @RunOnWorkThread
    public void start() {
        if (mIsFirstTime) {
            if (!NetworkUtils.isAvailable(App.sContext)) {
                postMainThread(() -> mView.showNetworkError());
                return;
            }
            if (CardMode.TAP.equals(mTransData.getCardMode())) {
                mSoundManager.play(SoundManager.Type.BRING_CARD);
            }
            //判断GPS信息
            /*mLog.debug("检查GPS信息");
            if ("1".equals(mRepository.getParamValue(PreferencesConstants.NEED_CHECK_GPS))) {
                mLog.debug("mTransData.getLatitude() {}",mRepository.getParamValue(PreferencesConstants.LATITUDE));
                if("0.0".equals(mRepository.getParamValue(PreferencesConstants.LATITUDE))||"0.0".equals(mRepository.getParamValue(PreferencesConstants.LONGITUDE))){
                    mView.showCheckGPSFail();
                    return;
                }
            }*/
            //判断基站信息
            if("0.0".equals(mRepository.getParamValue(PreferencesConstants.LATITUDE))||"0.0".equals(mRepository.getParamValue(PreferencesConstants.LONGITUDE))) {
                if (!AppUtils.isWifi()) {
                    if ("1".equals(mRepository.getParamValue(NEED_CHECK_BASE))) { //检查定位
                        if (mTransData.getTransType() != TransType.RESET_GPS && mTransData.getTransType() != TransType.LOGIN) {
                            if (!BassInfoUtil.checkBaseInfo(App.sContext)) {
                                mView.showCheckLocationFail();
                                return;
                            }
                        }
                    }
                }
            }

            int timeout = Integer.parseInt(StringUtils.firstNonEmpty(mRepository.getParamValue(PreferencesConstants.COMMUNICATION_TIMEOUT), "15"));
            postMainThread(() -> mView.setCountDown(timeout));
            //开始通讯
            sendTrans();
            mIsFirstTime = false;
        }
    }

    @Override
    public void pause() {

    }

    @Override
    public void destroy() {
        mSoundManager.stop();
        if (mComm != null) {
            mComm.dispose();
        }
    }

    private class TransObserver extends DisposableObserver<Object> {

        @Override
        public void onNext(@io.reactivex.annotations.NonNull Object recvData) {
            MessageData MessageData = (MessageData) recvData;
            //判断第一层响应码判断
            if(MessageData.getJsonRecData().getRSP_HEAD().getTRAN_SUCCESS().equals("1")){
                mMessageData.setJsonRecData(MessageData.getJsonRecData());
                postMainThread(() -> mView.showSuccessful(mMessageData));
            }else{
                if(MessageData.getJsonRecData().getRSP_HEAD().getERROR_MESSAGE()!= null){
                    postMainThread(() -> mView.showError(MessageData.getJsonRecData().getRSP_HEAD().getERROR_MESSAGE()));
                }else{
                    postMainThread(() -> mView.showOnlineError());
                }
            }
        }

        @Override
        public void onError(@io.reactivex.annotations.NonNull Throwable e) {
            if (e instanceof WriteException || e instanceof RequestException) {
                //这两种情况要删除已保存的记录
                if (mTransData.getTransType().isReversal() || mTransData.getTransType().isSaveRecord()) {
                    mLog.debug("删除本次交易数据");
                    mRepository.delLastTransRecord();
                }
                mLog.debug("连接出错");
                postMainThread(() -> mView.showConnectError());
            } else if (e instanceof ResponseException) {
                mLog.debug("接收超时");
                postMainThread(() -> mView.showReceiveTimeout());
            } else if (e instanceof ParseException) {
                mLog.debug("解包失败");
                postMainThread(() -> mView.showOnlineError());
            } else {
                mLog.debug("通讯异常");
                postMainThread(() -> mView.showOnlineError());
            }
            e.printStackTrace();
        }

        @Override
        public void onComplete() {
        }
    }


    private void communication(DisposableObserver<Object> observer) {
//        String ip = mRepository.getParamValue(PreferencesConstants.SERVER_IP);
//        String ip = "http://pbanktest.95559.com.cn/cipp/";
//        wzp add
        String dnsUrl = mRepository.getParamValue(PreferencesConstants.DNS_URL);
        int port = Integer.parseInt(StringUtils.firstNonEmpty(mRepository.getParamValue(PreferencesConstants.SERVER_PORT), "0000"));
        int timeout = Integer.parseInt(StringUtils.firstNonEmpty(mRepository.getParamValue(PreferencesConstants.COMMUNICATION_TIMEOUT), "15"));
        ChannelType type = ChannelType.SOCKET;
        if ("1".equals(mRepository.getParamValue(PreferencesConstants.ENABLE_SSL))) {
            type = ChannelType.HTTPS;
        }
//        ip = PaymentFactory.DnsProcess(url);
        mLog.debug("dnsUrl :{}",dnsUrl);
        PaymentPlatform repository = PaymentFactory.getInstance(type, dnsUrl, port, timeout);
        mComm = new SendAndRecv(repository);
        //组json数据
//        SendData sendData = DataBuilder.getInstance(mTransData, mRepository).build();
        mMessageData = DataBuilder.getInstance(mTransData,mRepository).buildJson();
//        mMessageData.setSendData(sendData);
//        mMessageData.setJsonSendData(sSendData);
        mComm.execute(observer, mMessageData);
    }

    private Upload.OnUploadListener mUploadListener = new Upload.OnUploadListener() {
        @Override
        public void onShowTip(String tip) {
            postMainThread(() -> mView.showTip(tip));
        }

        @Override
        public void onUploadFailed() {
            postMainThread(() -> mView.showOnlineQuit());
        }

        @Override
        public void onUploadComplete() {
            switch (mTask) {
                case SCRIPT:
                    doWhenScriptNotifyLoopEnds();
                    break;
                case REVERSAL:
                    doWhenReversalLoopEnds();
                    break;
       /*         case ES_NEVER_UPLOAD:
                    sendEsUploadFailedLoopStart(true);
                    break;
                case ES_UPLOAD_FAILED:
                    doWhenEsUploadFailedLoopEnds();
                    break;*/
                case MAG_OFFLINE:
                    sendIccOffline();
                    break;
                case ICC_OFFLINE:
                    doWhenOfflineUploadLoopEnds();
                    break;
 /*               case MAG_BATCH_OFFLINE:
                    sendBatchUploadIccOffline();
                    break;
                case ICC_BATCH_OFFLINE:
                    sendBatchUploadMagOnline();
                    break;
                case MAG_BATCH_ONLINE:
                    sendBatchUploadMagNotify();
                    break;
                case MAG_BATCH_NOTIFY:
                    sendBatchIccNotify();
                    break;
                case ICC_BATCH_NOTIFY:
                    sendBatchUploadIccOnline();
                    break;*/
                case ICC_BATCH_ONLINE:
                    sendBatchUploadIccOfflineDeclined();
                    break;
                case ICC_BATCH_OFFLINE_DECLINED:
                    sendBatchUploadIccOnlineArpcErr();
                    break;
                case ICC_BATCH_ONLINE_ARPC:
                    doWhenBatchUploadLoopEnds();
                    break;
                default:
                    sendCurrTrans();
                    break;
            }

        }
    };

    /**
     * 脚本结果通知上送一个大循环结束后的逻辑判断
     */
    private void doWhenScriptNotifyLoopEnds() {
        mLog.debug("脚本结果通知上送一个大循环结束");
        //重发循环次数到了则发冲正，否则开启下一次循环
        if (!mUpload.isOverMaxReUploadTimes()) {
            mLog.debug("继续循环上送");
            sendScriptNotifyLoopStart(false);
        } else {
            sendReversalLoopStart(true);
        }
    }

    /**
     * 冲正上送一个大循环结束后的逻辑判断
     */
    private void doWhenReversalLoopEnds() {
        mLog.debug("冲正上送一个大循环结束");
        //重发循环次数到了则发冲正，否则开启下一次循环
        if (!mUpload.isOverMaxReUploadTimes()) {
            mLog.debug("继续循环上送");
            sendReversalLoopStart(false);
        } else {
            //不同交易类型，在发提前包时的逻辑顺序不同
            if (TransType.SETTLE.equals(mTransData.getTransType())) {
                //结算交易之前在上送电子签名还有脱机交易
//                sendEsNeverEverUpload();
                sendOfflineLoopStart(true);
            } else if (TransType.BATCH_END.equals(mTransData.getTransType())) {
                //批上送结束报文前要看看有没有必要批上送
                sendBatchUploadUploadLoopStart(true);
            } else {
                //普通交易
                sendCurrTrans();
            }
        }
    }

    /**
     * 上送之前上送失败的电子签名，一个大循环结束后的逻辑判断
     */
    private void doWhenEsUploadFailedLoopEnds() {
        mLog.debug("上送失败的电子签名上送一个大循环结束");
        //重发循环次数到了则发离线/脱机交易，否则开启下一次循环
        if (!mUpload.isOverMaxReUploadTimes()) {
            mLog.debug("继续循环上送");
            sendEsUploadFailedLoopStart(false);
        } else {
            sendOfflineLoopStart(true);
        }
    }

    /**
     * 离线/脱机交易上送一个大循环结束后的逻辑判断
     */
    private void doWhenOfflineUploadLoopEnds() {
        mLog.debug("离线上送一个大循环结束");
        //重发循环次数到了则发电子签名，否则开启下一次循环
        if (!mUpload.isOverMaxReUploadTimes()) {
            mLog.debug("继续循环上送");
            sendOfflineLoopStart(false);
        } else {
            //lr
            if (TransType.SETTLE.equals(mTransData.getTransType())) {
                //批上送结束报文前要看看有没有必要批上送
                sendBatchUploadUploadLoopStart(true);
            } else {
                //普通交易
                sendCurrTrans();
            }
        }
    }

    /**
     * 批上送一个大循环结束后的逻辑判断
     */
    private void doWhenBatchUploadLoopEnds() {
        mLog.debug("批上送一个大循环结束");
        //批上送循环次数到了则发结束报文，否则开启下一次循环
        if (!mUpload.isOverMaxReUploadTimes()) {
            mLog.debug("继续循环上送");
            sendBatchUploadUploadLoopStart(false);
        } else {
//            wzp delete
//            updateSettleInfoBatchUploadState();
            sendCurrTrans();
        }
    }

    private void sendEsUploadFailedLoopStart(boolean firstTime) {
        initUploadCounter(firstTime);
        sendEsUploadFailed();
    }

    /**
     * 上送之前上送失败的电子签名
     */
    private void sendEsUploadFailed() {
        mTask = Upload.NowTask.ES_UPLOAD_FAILED;
        newUploadInstance();
    }

    /**
     * 更新结算数据库信息
     */
    private void updateSettleInfoBatchUploadState() {
        SettleInfo settleInfo = mRepository.getSettleInfo();
        settleInfo.setIsBatchUploadComplete(true);
        mRepository.updateSettleInfo(settleInfo);
    }

    /**
     * 批上送开始，最开始清除计数器
     *
     * @param firstTime 是否第一次
     */
    private void sendBatchUploadUploadLoopStart(boolean firstTime) {
        initUploadCounter(firstTime);
        sendBatchUpload();
    }

    /**
     * 批上送，如重发时调用，不清除计数器
     */
    private void sendBatchUpload() {
        sendBatchUploadIccOnline();
/*        SettleInfo settleInfo = mRepository.getSettleInfo();
        if (!settleInfo.getIsEqu() || !settleInfo.getIsInterEqu()) {
            sendBatchUploadMagOffline();
        } else {
            sendBatchUploadIccOnline();
        }*/
    }

    /**
     * 批上送IC卡脱机
     */
    private void sendBatchUploadIccOffline() {
        mTask = Upload.NowTask.ICC_BATCH_OFFLINE;
        newUploadInstance();
    }

    /**
     * 批上送磁条卡离线
     */
    private void sendBatchUploadMagOffline() {
        mTask = Upload.NowTask.MAG_BATCH_OFFLINE;
        newUploadInstance();
    }

    /**
     * 上送未上送过的电子签名
     */
    private void sendEsNeverEverUpload() {
        mTask = Upload.NowTask.ES_NEVER_UPLOAD;
        newUploadInstance();
    }

    /**
     * 批上送IC卡联机ARPC错
     */
    private void sendBatchUploadIccOnlineArpcErr() {
        mTask = Upload.NowTask.ICC_BATCH_ONLINE_ARPC;
        newUploadInstance();
    }

    /**
     * 批上送IC卡脱机拒绝
     */
    private void sendBatchUploadIccOfflineDeclined() {
        mTask = Upload.NowTask.ICC_BATCH_OFFLINE_DECLINED;
        newUploadInstance();
    }

    /**
     * 批上送IC卡联机
     */
    private void sendBatchUploadIccOnline() {
        mTask = Upload.NowTask.ICC_BATCH_ONLINE;
        newUploadInstance();
    }

    /**
     * 批上送IC卡通知
     */
    private void sendBatchIccNotify() {
        mTask = Upload.NowTask.ICC_BATCH_NOTIFY;
        newUploadInstance();
    }

    /**
     * 批上送磁条卡通知
     */
    private void sendBatchUploadMagNotify() {
        mTask = Upload.NowTask.MAG_BATCH_NOTIFY;
        newUploadInstance();
    }

    /**
     * 批上送磁条卡联机
     */
    private void sendBatchUploadMagOnline() {
        mTask = Upload.NowTask.MAG_BATCH_ONLINE;
        newUploadInstance();
    }

    /**
     * IC卡脱机上送
     */
    private void sendIccOffline() {
        mTask = Upload.NowTask.ICC_OFFLINE;
        newUploadInstance();
    }
    /**
     * 磁条卡离线上送
     */
    private void sendMagOffline() {
        mTask = Upload.NowTask.MAG_OFFLINE;
        newUploadInstance();
    }

    /**
     * 开始上送离线/脱机交易
     *
     * @param firstTime 是否第一次
     */
    private void sendOfflineLoopStart(boolean firstTime) {
        initUploadCounter(firstTime);
        sendMagOffline();
    }

    /**
     * 发送脚本结果通知
     */
    private void sendScriptNotify() {
        mTask = Upload.NowTask.SCRIPT;
        newUploadInstance();
    }

    /**
     * 发送冲正
     */
    private void sendReversal() {
        mTask = Upload.NowTask.REVERSAL;
        newUploadInstance();
    }

    /**
     * 获取上送实例
     */
    private void newUploadInstance() {
        mUpload = Upload.getInstance(mTask, mRepository);
        mUpload.sendTrans(mUploadListener);
    }

    private void sendScriptNotifyLoopStart(boolean firstTime) {
        initUploadCounter(firstTime);
        sendScriptNotify();
    }

    private void sendReversalLoopStart(boolean firstTime) {
        initUploadCounter(firstTime);
        sendReversal();
    }

    private void initUploadCounter(boolean firstTime) {
        if (firstTime) {
            mLog.debug("初始上送计数器为1");
            Upload.sUploadCounter = 1;
        } else {
            Upload.sUploadCounter++;
            mLog.debug("上送计数器自增{}", Upload.sUploadCounter);
        }
    }

    /**
     * 开始发送交易，根据条件判断发送提前包，还是当前包
     */
    private void sendTrans() {
        //需要发送提前包，如冲正、脚本等
        if (mTransData.getTransType().isPreSend()) {
            sendUpload();
        } else {
            sendCurrTrans();
        }
    }

    /**
     * 开始上送
     */
    private void sendUpload() {
        //脚本是所有的上送里面第一个上送的
        sendScriptNotifyLoopStart(true);
    }

    /**
     * 发送当前交易
     */
    private void sendCurrTrans() {
        mLog.debug("当前交易[{}]", mTransData.getTransType().getName() > 0 ? App.sContext.getString(mTransData.getTransType().getName()) : mTransData.getTransType());
        if (mTransData.getTransType().isPreSend()){
            postMainThread(() -> mView.showTip(App.sContext.getString(R.string.pls_wait)));
        }
        communication(new TransObserver());
    }
}